import Observer from './observer'
import Watcher from './watcher';

export function initState(vm){
  // 做不同的初始化工作
  let opts = vm.$options;
  if(opts.data){
    initData(vm)
  }
  if (opts.computed) {
    initComputed(vm, opts.computed)
  }
  if (opts.watch) {
    initWatch(vm)
  }
}

//-------------------初始化data----------------------------
/**
 * 会将对vm.data的取值和赋值操作，代理给vm._data上
 * vm.msg 等价于 vm._data.msg
 */
function proxy(vm, source, key){
  Object.defineProperty(vm, key, {
    get(){
      return vm[source][key]
    },

    set(newVal){
      vm[source][key] = newVal
    }
  })
}

export function observe(data){
  if (typeof data !== 'object' || data == null) {
    // 不是对象，或者是null就不执行下面的逻辑
    return
  }

  return new Observer(data)
}

// 将用户传入的数据  通过Object.definProperty重新定义
function initData(vm){
  let data = vm.$options.data;
  // data可能是函数，或者是对象
  data = vm._data = typeof data === 'function' ? data.call(vm) : data || {}

  for(let key in data){
    proxy(vm, '_data', key)
  }

  observe(vm._data); // 观察数据
}

//---------------------初始化Computed--------------------------

// 初始化computed方法
function initComputed(vm, computed){
  // 创建存储计算属性的watcher对象, 将计算属性的配置放到vm上
  let watchers = vm._watchersComputed = Object.create(null);
  for(let key in computed){
    
    // lazy 表示是计算属性 
    watchers[key] = new Watcher(vm, computed[key], null, {lazy: true});

    // vm.fullname 将这个计算属性定义到vm上面
    Object.defineProperty(vm, key, {
      get: createComputedGetter(vm, key)
    })
  }
}


function createComputedGetter(vm, key){
  let watcher = vm._watchersComputed[key];
  // 用户在取值时会执行这个方法
  return function(){
    if (watcher) {
      // 如果dirty是false 就用缓存的值
      if (watcher.dirty) {// 如果页面取值，dirty是true，调用watcher的get方法
        watcher.evaluate();
      }
      // 
      if (Dep.target) {
        watcher.depend();
      }
      return watcher.value;
    }
  }
}

//-------------------初始化watch----------------------------

function createWatcher(vm, key, handler){
  // 内部最终也会使用$watch方法
  return vm.$watch(key, handler);
}

function initWatch(vm){
  let watch = vm.$options.watch;
  for(let key in watch){
    let handler = watch[key];

    createWatcher(vm, key, handler);
  }

}